/*
* Copyright 2013-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.aws.messaging.config.xml;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.cloud.aws.context.config.xml.GlobalBeanDefinitionUtils;
import org.springframework.cloud.aws.messaging.core.QueueMessagingTemplate;
import org.springframework.cloud.aws.messaging.listener.QueueMessageHandler;
import org.springframework.cloud.aws.messaging.listener.SendToHandlerMethodReturnValueHandler;
import org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer;
import org.springframework.core.Conventions;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import static org.springframework.cloud.aws.messaging.config.xml.BufferedSqsClientBeanDefinitionUtils.getCustomAmazonSqsClientOrDecoratedDefaultSqsClientBeanName;
/**
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} for the <annotation-driven-queue-listener/>
* element.
*
* @author Agim Emruli
* @author Alain Sahli
* @since 1.0
*/
public class AnnotationDrivenQueueListenerBeanDefinitionParser extends AbstractBeanDefinitionParser {
private static final String TASK_EXECUTOR_ATTRIBUTE = "task-executor";
private static final String MAX_NUMBER_OF_MESSAGES_ATTRIBUTE = "max-number-of-messages";
private static final String VISIBILITY_TIMEOUT_ATTRIBUTE = "visibility-timeout";
private static final String WAIT_TIME_OUT_ATTRIBUTE = "wait-time-out";
private static final String AUTO_STARTUP_ATTRIBUTE = "auto-startup";
private static final String DESTINATION_RESOLVER_ATTRIBUTE = "destination-resolver";
private static final String BACK_OFF_TIME = "back-off-time";
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder containerBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMessageListenerContainer.class);
if (StringUtils.hasText(element.getAttribute(TASK_EXECUTOR_ATTRIBUTE))) {
containerBuilder.addPropertyReference(Conventions.attributeNameToPropertyName(TASK_EXECUTOR_ATTRIBUTE), element.getAttribute(TASK_EXECUTOR_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(MAX_NUMBER_OF_MESSAGES_ATTRIBUTE))) {
containerBuilder.addPropertyValue(Conventions.attributeNameToPropertyName(MAX_NUMBER_OF_MESSAGES_ATTRIBUTE), element.getAttribute(MAX_NUMBER_OF_MESSAGES_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(VISIBILITY_TIMEOUT_ATTRIBUTE))) {
containerBuilder.addPropertyValue(Conventions.attributeNameToPropertyName(VISIBILITY_TIMEOUT_ATTRIBUTE), element.getAttribute(VISIBILITY_TIMEOUT_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(WAIT_TIME_OUT_ATTRIBUTE))) {
containerBuilder.addPropertyValue(Conventions.attributeNameToPropertyName(WAIT_TIME_OUT_ATTRIBUTE), element.getAttribute(WAIT_TIME_OUT_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(AUTO_STARTUP_ATTRIBUTE))) {
containerBuilder.addPropertyValue(Conventions.attributeNameToPropertyName(AUTO_STARTUP_ATTRIBUTE), element.getAttribute(AUTO_STARTUP_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE))) {
containerBuilder.addPropertyReference(Conventions.attributeNameToPropertyName(DESTINATION_RESOLVER_ATTRIBUTE), element.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE));
}
if (StringUtils.hasText(element.getAttribute(BACK_OFF_TIME))) {
containerBuilder.addPropertyValue(Conventions.attributeNameToPropertyName(BACK_OFF_TIME), element.getAttribute(BACK_OFF_TIME));
}
String amazonSqsClientBeanName = getCustomAmazonSqsClientOrDecoratedDefaultSqsClientBeanName(element, parserContext);
containerBuilder.addPropertyReference(Conventions.attributeNameToPropertyName("amazon-sqs"), amazonSqsClientBeanName);
containerBuilder.addPropertyReference("resourceIdResolver", GlobalBeanDefinitionUtils.retrieveResourceIdResolverBeanName(parserContext.getRegistry()));
containerBuilder.addPropertyReference("messageHandler", getMessageHandlerBeanName(element, parserContext, amazonSqsClientBeanName));
return containerBuilder.getBeanDefinition();
}
@Override
protected boolean shouldGenerateId() {
return true;
}
private static String getMessageHandlerBeanName(Element element, ParserContext parserContext, String sqsClientBeanName) {
BeanDefinitionBuilder queueMessageHandlerDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(QueueMessageHandler.class);
ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
if (!argumentResolvers.isEmpty()) {
queueMessageHandlerDefinitionBuilder.addPropertyValue("customArgumentResolvers", argumentResolvers);
}
ManagedList<BeanDefinition> returnValueHandlers = getReturnValueHandlers(element, parserContext);
returnValueHandlers.add(createSendToHandlerMethodReturnValueHandlerBeanDefinition(element, parserContext, sqsClientBeanName));
queueMessageHandlerDefinitionBuilder.addPropertyValue("customReturnValueHandlers", returnValueHandlers);
String messageHandlerBeanName = parserContext.getReaderContext().generateBeanName(queueMessageHandlerDefinitionBuilder.getBeanDefinition());
parserContext.getRegistry().registerBeanDefinition(messageHandlerBeanName, queueMessageHandlerDefinitionBuilder.getBeanDefinition());
return messageHandlerBeanName;
}
private static AbstractBeanDefinition createSendToHandlerMethodReturnValueHandlerBeanDefinition(Element element, ParserContext parserContext, String sqsClientBeanName) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(SendToHandlerMethodReturnValueHandler.class);
if (StringUtils.hasText(element.getAttribute("send-to-message-template"))) {
beanDefinitionBuilder.addConstructorArgReference(element.getAttribute("send-to-message-template"));
} else {
// TODO consider creating a utils for setting up the queue messaging template as it also created in QueueMessagingTemplateBeanDefinitionParser
BeanDefinitionBuilder templateBuilder = BeanDefinitionBuilder.rootBeanDefinition(QueueMessagingTemplate.class);
templateBuilder.addConstructorArgReference(sqsClientBeanName);
templateBuilder.addConstructorArgReference(GlobalBeanDefinitionUtils.retrieveResourceIdResolverBeanName(parserContext.getRegistry()));
beanDefinitionBuilder.addConstructorArgValue(templateBuilder.getBeanDefinition());
}
return beanDefinitionBuilder.getBeanDefinition();
}
private static ManagedList<BeanDefinition> getArgumentResolvers(Element element, ParserContext parserContext) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
if (resolversElement != null) {
return extractBeanSubElements(resolversElement, parserContext);
} else {
return new ManagedList<>(0);
}
}
private static ManagedList<BeanDefinition> getReturnValueHandlers(Element element, ParserContext parserContext) {
Element handlersElement = DomUtils.getChildElementByTagName(element, "return-value-handlers");
if (handlersElement != null) {
return extractBeanSubElements(handlersElement, parserContext);
} else {
return new ManagedList<>(0);
}
}
private static ManagedList<BeanDefinition> extractBeanSubElements(Element parentElement, ParserContext parserContext) {
ManagedList<BeanDefinition> list = new ManagedList<>();
list.setSource(parserContext.extractSource(parentElement));
for (Element beanElement : DomUtils.getChildElementsByTagName(parentElement, "bean")) {
BeanDefinitionHolder beanDef = parserContext.getDelegate().parseBeanDefinitionElement(beanElement);
beanDef = parserContext.getDelegate().decorateBeanDefinitionIfRequired(beanElement, beanDef);
list.add(beanDef.getBeanDefinition());
}
return list;
}
}